package domain;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Observable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

/**
 * This class takes care of HTTP related requests.<br>For handling HTTPS related requests, please see the documentation for the DragonEngineS class.
 * @author HTTPDragon Team
 */
    @SuppressWarnings("LoggerStringConcat")
public class DragonEngine extends Observable implements Runnable {
    private ExecutorService executor;
    private boolean running;
    private ServerSocket server;
    private static final String SERVICENAME = "(HTTP)  ";
    private int port;
    private ServerConfig config;
    private static final Logger logger = Logger.getLogger("HTTPDragon Log");
        
    /**
     * This method gets the current HTTP port number.
     * @return The HTTP port number.
     */
    public int getPort() {
        return port;
    }
    private void setPort(int Port) {
        this.port = Port;
    }
    private void setConfig(ServerConfig Config){
        this.config = Config;
    }

    /**
     * Public Constructor for creating the DragonEgine
     * @param config a server configuration.
     */
    public DragonEngine(ServerConfig config) {
        setPort(config.getHttpPort());
        setConfig(config);
    }
    
    /**
     * This method determines whether or not the HTTP module is running.
     * @return true or false.
     */
    public boolean isRunning(){
        return running;
    }
    
    /**
     * This method starts the HTTP module, so it can start processing requests.<br>Does not process HTTPS requests.
     */
    @Override
    public void run(){  
            try {
                if(config.isHttpRun()){
                    server = new ServerSocket(getPort());
                    setRunningTrue();
                    logger.info(SERVICENAME + "Service started, html source folder at " + config.getHttpRootPath());
                    logger.info(SERVICENAME + "Default Index name has been set to " + config.getHttpDefaultPage());            
                    logger.info(SERVICENAME + "Listening at port " + getPort() + " ...");
                    executor = Executors.newCachedThreadPool();
                    while (true) {                        
                        Socket socket = server.accept();                        
                        // nieuwe clienthandler thread op executorservice zetten voor uitvoering
                        executor.execute(new ClientHandler(socket, config));
                    }      
                }else{
                    setRunningFalse();
                }        
            }
            catch(SocketException ex){
                switch (ex.toString()) {
                    case "java.net.SocketException: socket closed":
                        //nothing because 'Socket closed' exception is expected by us on shutdown()                    
                        break;
                    case "java.net.SocketException: Connection reset by peer: socket write error":
                        // Connection was closed by the browser while requesting
                        logger.info(SERVICENAME+"Connection reset by peer.");
                        break;
                    case "java.net.SocketException: Software caused connection abort: socket write error":
                        logger.info(SERVICENAME+"Software caused a connection abort.");
                        break;
                    case "java.net.BindException: Address already in use: JVM_Bind":
                        logger.info(SERVICENAME+"Port "+port+" is already in use by an other program.");
                        running=false;
                        break;
                    default:
                        System.err.println(ex.getMessage());
                        break;
                }
            }
            catch (IOException ex) {            
                System.err.println(ex);
            }
    }
    private void setRunningFalse(){
            running = false;
            this.setChanged();
            this.notifyObservers();    
    }
    private void setRunningTrue(){
            running = true;
            this.setChanged();
            this.notifyObservers(); 
    }
    
    /**
     * This method stops the HTTP module.<br>Does not stop the HTTPS module.
     */
    public void shutdown(){
        try {
            if(running){
            executor.shutdown(); //huidige open clienthandler thread(s) afsluiten
            setRunningFalse();
            server.close(); //niet meer luisteren op opgegeven poort
            logger.info(SERVICENAME+"Stopped listening at port " + port);
            }
        } catch (IOException ex) {            
            System.err.println(ex);
        }
    }   
}
